#!/usr/bin/env python3

from threading import Timer

# 案例
class Writer:
    def __init__(self):
        self.program_num = 0  # 问题来了，被多个程序调用的

    def log_to_file(self, message: str):
        print(message)
        self.program_num += 1
        print("There are total {} times calls".format(self.program_num))


class Program_Manual_Control:
    cls_id = 0
    def __init__(self, loop, separator, writer):
        self.id = self.get_program_id()

        self.index = 0
        self.looptime = loop
        self.separator = separator

        self.writer = writer
        self.exec()

    @classmethod
    def get_program_id(cls):
        cls.cls_id += 1
        return cls.cls_id

    def exec(self):
        self.index += 1
        timer = Timer(self.separator, self.step)
        timer.start()

    def step(self):
        message = "Program【{}】 write down ({}/{})".format(self.id, self.index, self.looptime)
        self.writer.log_to_file(message)
        if self.index < self.looptime:
            self.exec()

def TEST_FOR_MANUAL_CONTROL():
    p0 = Program_Manual_Control(5, 0.5, Writer())
    p1 = Program_Manual_Control(3, 1.2, Writer())
    return p0, p1


# 单例模式
class Program_Singleton:
    def __new__(cls, *args):
        # usually, this static method return super().__new__(cls); but here:
        if not hasattr(cls, "instance"):
            cls.instance = super().__new__(cls)
            cls.instance_override = False  # here, you should choose to use this or MagicMethod
            if cls.instance_override == False:
                cls.instance.initialize_singleton(*args)
        return cls.instance

    def __init__(self, attrib: str, num: int):
        # 没办法屏蔽该方法的运行
        # 且每次new操作，该方法都会被调用
        # 使用不当将造成单例属性的改写
        if self.instance_override == True:
            print("self.__init__() is called : attrib[{}], num[{}]".format(attrib, num))
            self.attrib = attrib
            self.number = num  # 新建属性
        elif True:  # or just ignore this renew-operation, BY comment this two lines.
            raise Exception("The Singleton-Class should not override the self.__init__()")

    def initialize_singleton(self, attrib: str, num: int):
        # 换用自定义方法，则仅在对象第一次创建时执行初始化
        print("self.initialize() is called : attrib[{}], num[{}]".format(attrib, num))
        self.attrib = attrib

def TEST_FOR_SINGLETON():
    p0 = Program_Singleton("abc", 123)
    p1 = Program_Singleton("def", 456)
    return p0, p1


# 对Program_Singleton进行优化（仅限于格式上的）
class Program_Singleton2:
    def __new__(cls, *args):
        # usually, this static method return super().__new__(cls); but here:
        if not hasattr(cls, "__hasinstance"):
            cls.instance = super().__new__(cls)
            cls.__hasinstance = False
        return cls.instance

    def __init__(self, attrib: str, num: int):
        if self.__hasinstance == False:
            self.__hasinstance = True

            # the singleton initialize
            print("singleton initialize : attrib[{}], num[{}]".format(attrib, num))
            self.attrib = attrib
            self.number = num  # 新建属性
        else:
            raise Exception("The Singleton-Class should not override the self.__init__()")
            # or pass directly. so u can get instance by: new Program_Singleton2()

    @classmethod
    def get_instance(cls):
        return cls.instance  # or self, it's the same

def TEST_FOR_SINGLETON2():
    p0 = Program_Singleton2("abc", 123)  # or : p0=Program_Singleton2("abc", 123).get_instance()
    p1 = Program_Singleton2.get_instance()  # 显示区分单例对象的创建与获取
    return p0, p1


class Program_Singleton3:
    singleton_attribute = {}

    def __init__(self, attrib: str, num: int):
        if self.__dict__:
            raise Exception("对象属性字典初始化前不为空，请查看详情")
        self.__dict__ = self.singleton_attribute
        if not self.singleton_attribute:  # 若属性为空，则当前尚未实例化Singleton对象
            # initialize here
            print("self.__init__() is called : attrib[{}], num[{}]".format(attrib, num))
            self.attrib = attrib
            self.number = num

def TEST_FOR_SINGLETON3():
    p0 = Program_Singleton3("abc", 123)
    p1 = Program_Singleton3("def", 456)

    print("p0.attrib before p0-changing -> %s" % p0.attrib)
    print("p1.attrib before p0-changing -> %s" % p0.attrib)
    p0.attrib = "【NEW atrrib of p0】"  # 修改了p0的属性
    print("p1.attrib -> %s" % p0.attrib)  # p1属性也被修改了，所以实际上p0与p1所操作的对象是同一个

    return p0, p1


if __name__ == "__main__":
    # p0, p1 = TEST_FOR_MANUAL_CONTROL()
    # p0, p1 = TEST_FOR_SINGLETON()
    # p0, p1 = TEST_FOR_SINGLETON2()
    p0, p1 = TEST_FOR_SINGLETON3()

    def item_info(obj, name):
        print("The id of {n}: {}, the type of {n}: {}, the attrib of {n}: {}.".format(id(obj), type(obj), obj.attrib, n=name))

    item_info(p0, "p0")
    item_info(p1, "p1")

    if id(p0) == id(p1):  print("Same Object.")
    else:  print("Different Object.")
